package classfile

import (
	"fmt"
)

type MemberInfo struct {
	cp              ConstantPool
	accessFlags     uint16
	nameIndex       uint16
	descriptorIndex uint16
	//attributes      []Attribute
}

type ClassFile struct {
	magic        uint32
	minorVersion uint16
	majorVersion uint16
	constantPool ConstantPool
	accessFlags  uint16
	thisClass    uint16
	superClass   uint16
	interfaces   []uint16
	fields       []*MemberInfo
	methods      []*MemberInfo
	//attributes   []AttributeInfo
}

func Parse(classData []byte) (cf *ClassFile, err error) {
	defer func() {
		if r := recover(); r != nil {
			var ok bool
			err, ok = r.(error)
			if !ok {
				err = fmt.Errorf("%v", r)
			}
		}
	}()
	cr := &ClassReader{classData}
	cf = &ClassFile{}
	cf.read(cr)
	return
}

func (self *ClassFile) read(reader *ClassReader) {
	// 检查魔数
	self.readAndCheckMagic(reader)

	// 检查版本号
	self.readAndCheckVersion(reader)

	// 读取常量池
	self.constantPool = readConstantPool(reader)

	fmt.Println("---------------------------------")
}

func (self *ClassFile) readAndCheckMagic(reader *ClassReader) {
	self.magic = reader.readUint32()
	if self.magic != 0xCAFEBABE {
		panic("java.lang.ClassFormatError: magic!")
	}
}

func (self *ClassFile) readAndCheckVersion(reader *ClassReader) {
	self.minorVersion = reader.readUint16()
	self.majorVersion = reader.readUint16()

	fmt.Printf("%d %d\n", self.minorVersion, self.majorVersion)
	switch self.majorVersion {
	case 45:
		return
	case 46, 47, 48, 49, 50, 51, 52:
		if self.minorVersion == 0 {
			return
		}
	}
	panic("java.lang.UnspoortedClassVersionError!")
}

// getter
func (self *ClassFile) MinorVersion() uint16 {
	return self.minorVersion
}

// getter
func (self *ClassFile) MajorVersion() uint16 {
	return self.majorVersion
}

// getter
func (self *ClassFile) ConstantPool() ConstantPool {
	return self.constantPool
}

// getter
func (self *ClassFile) AccessFlags() uint16 {
	return self.accessFlags
}

// getter
func (self *ClassFile) Fields() []*MemberInfo {
	return self.fields
}

// getter
func (self *ClassFile) Methods() []*MemberInfo {
	return self.methods
}

// getter
func (self *ClassFile) ClassName() string {
	return "aa1"
}

// getter
func (self *ClassFile) SuperClassName() string {
	return "aa2"
}

// getter
func (self *ClassFile) InterfaceNames() []string {
	return []string{"a3", "a4"}
}

func readConstantInfo(reader *ClassReader, cp ConstantPool) ConstantInfo {
	tag := reader.readUint8()
	c := newConstantInfo(tag, cp)
	c.readInfo(reader)
	return c
}

func newConstantInfo(tag uint8, cp ConstantPool) ConstantInfo {
	switch tag {
	case CONSTANT_Integer:
		return &ConstantIntegerInfo{}
	case CONSTANT_Float:
		return &ConstantFloatInfo{}
	case CONSTANT_Long:
		return &ConstantLongInfo{}
	case CONSTANT_Double:
		return &ConstantDoubleInfo{}
	case CONSTANT_Utf8:
		return &ConstantUtf8Info{}
	case CONSTANT_String:
		return &ConstantStringInfo{cp: cp}
	case CONSTANT_Class:
		return &ConstantClassInfo{cp: cp}
	case CONSTANT_Fieldref:
		return &ConstantFieldrefInfo{ConstantMemberrefInfo{cp: cp}}
	case CONSTANT_Methodref:
		return &ConstantMethodrefInfo{ConstantMemberrefInfo{cp: cp}}
	case CONSTANT_InterfaceMethodref:
		return &ConstantInterfaceMethodrefInfo{ConstantMemberrefInfo{cp: cp}}
	case CONSTANT_NameAndType:
		return &ConstantNameAndType{}
	case CONSTANT_MethodType:
		return &ConstantMethodrefInfo{}
	//case CONSTANT_MethodHandle:
	//	return &ConstantMethodHandleInfo{}
	//case CONSTANT_InvokeDynamic:
	//	return &ConstantInvokeDynamicInfo{}
	default:
		fmt.Printf("%v\n", tag)
		panic("java.lang.ClassFormatError: constant pool tag!")
	}
}
